#include "inc.h"

#define TAG_SERVER "SERVER"

bool server(uint16_t port, void *udata
    , std::function<bool(SOCKET accept_fd, void *udata)> onaccept
    , std::function<void(SOCKET listen_fd, void *udata)> onlisten) {
  if (!onaccept) return false;
  bool ret = false;
  bool quit = false;
  sockaddr_in addr = { 0 };

  SOCKET fd_listen = socket(AF_INET, SOCK_STREAM, 0);
  if (fd_listen < 0) {
    MY_LOG(TAG_SERVER, "create socket error.");
    goto lb_err;
  }

  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = htonl(INADDR_ANY);
  addr.sin_port = htons(port);
  if (bind(fd_listen, (const sockaddr*)&addr, sizeof(addr)) != NOERROR) {
    MY_LOG(TAG_SERVER, "bind error.");
    goto lb_err;
  }

  if (listen(fd_listen, 5) != NOERROR) {
    MY_LOG(TAG_SERVER, "listen error.");
    goto lb_err;
  }
  
  MY_LOG(TAG_SERVER, "listen in 0.0.0.0: %d.", port);

  if (onlisten)
    onlisten(fd_listen, udata);

  ret = true;
  while (!quit) {
    struct sockaddr_in addr_from;
    socklen_t addr_from_len = sizeof(addr_from);
    SOCKET fd_conn = accept(fd_listen, (struct sockaddr *)&addr_from, &addr_from_len);
    if (fd_conn < 0) {
      MY_LOG(TAG_SERVER, "accept error.");
      ret = false;
      goto lb_finish;
    }

    quit = !onaccept(fd_conn, udata);

   lb_finish:
    if (fd_conn >= 0) closesocket(fd_conn);
  }

 lb_err:
  if (fd_listen >= 0) closesocket(fd_listen);
  return ret;
}
